/*
合理的触发响应
1. 当值没有发生改变时，不需要触发effectFn
2. 原型上继承属性的情况
*/
import {
  track,
  trigger,
  ITERATE_KEY,
  TriggerType,
  effect,
} from './reactive-system'

function reactive(obj) {
  return new Proxy(obj, {
    // in 操作的拦截
    has(target, key) {
      track(target, key)
      return Reflect.has(target, key)
    },
    // for... in ...拦截
    ownKeys(target) {
      track(target, ITERATE_KEY)
      return Reflect.ownKeys(target)
    },
    // 拦截删除属性操作
    deleteProperty(target, key) {
      const hasKey = Object.prototype.hasOwnProperty.call(target, key) // 判断是否是自身属性
      const res = Reflect.deleteProperty(target, key)
      if (res && hasKey) {
        trigger(target, key, TriggerType.DELETE)
      }
      return res
    },
    set(target, key, val, receiver) {
      const type = Object.prototype.hasOwnProperty.call(target, key)
        ? TriggerType.SET
        : TriggerType.ADD // 要区分是新增属性还是修改属性
      const oldVal = target[key]
      const res = Reflect.set(target, key, val, receiver)
      // 代理对象指向就是原对象的时候， 从而达到屏蔽原型引起的重复更新
      if (receiver.raw === target) {
        // 当新旧值不同且都不是NaN的时候，触发响应
        if (oldVal !== val && !isNaN(oldVal) && !isNaN(val)) {
          trigger(target, key, type)
        }
      }
      return res
    },
    get(target, key, receiver) {
      // 方便合理触发响应，判断receiver.raw
      if (key === 'raw') {
        return target
      }
      track(target, key)
      return Reflect.get(target, key, receiver)
    },
  })
}

// 原型的问题
const obj = {}
const proto = { bar: 1 }
const child = reactive(obj)
const parent = reactive(proto)
Object.setPrototypeOf(child, parent)
effect(() => {
  console.log(child.bar)
})

/*
child修改parent的bar, 会导致effectFn 执行两次，需要屏蔽其中的一次
child上其实是没有bar属性的，因此在触发set的时候， 首先触发target为obj的set， 然后触发target为proto的set
两次的target不同，但是receiver都是代理对象child!!!!!!
因此，可以通过判断 receiver代理的对象和target是不是一样的，来触发响应
*/
child.bar = 2
